package com.nlelpct.manage.filter;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.nlelpct.manage.constant.ResponseCode;
import com.nlelpct.manage.constant.SecurityConstant;
import com.nlelpct.manage.context.ThreadParameter;
import com.nlelpct.manage.enums.ResultEnum;
import com.nlelpct.manage.model.security.MyUser;
import com.nlelpct.manage.model.vo.ResponseResult;
import com.nlelpct.manage.utils.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static com.nlelpct.manage.constant.SystemConstant.*;

/**
 * @author :小马
 * @date : 2023-07-12
 */
@Slf4j
public class AccreditFilter extends OncePerRequestFilter {
    private final RedisTemplate<String, Object> redisTemplate;

    public AccreditFilter(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 权限相关的操作
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestUri = request.getRequestURI();
        setLocalThreadParameter(request);
        for (String url : SecurityConstant.NO_AUTHORIZE_URL_LIST) {
            if (requestUri.equals(url) || requestUri.contains(SecurityConstant.DRUID_URL)) {
                doFilter(request, response, filterChain);
                return;
            }
        }
        String token = request.getHeader(SecurityConstant.SECURITY_TOKEN);
        if (ObjectUtil.isEmpty(token)) {
            ResponseUtils.out(response, ResponseResult.error(ResultEnum.TOKEN_FAILED));
            return;
        }
        try {
            String tokenString = Objects.requireNonNull(redisTemplate.opsForValue().get(token)).toString();
            MyUser myUser = JSON.parseObject(tokenString, MyUser.class);
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                    new UsernamePasswordAuthenticationToken(myUser, token, myUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            redisTemplate.opsForValue().set(token, JSON.toJSONString(myUser), SecurityConstant.TOKEN_TIME, TimeUnit.MINUTES);
            redisTemplate.opsForValue().set(myUser.getUserId(), token, SecurityConstant.TOKEN_TIME, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("校验token出现异常:{} ", e.getLocalizedMessage());
            ResponseUtils.out(response, ResponseResult.error(ResultEnum.TOKEN_EXPIRED));
            return;
        }
        try {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            e.printStackTrace();
            ResponseUtils.out(response, ResponseResult.error(ResponseCode.ORDINARY_FAIL, "出现了未知错误,请联系系统管理员"));
        }
    }

    private void setLocalThreadParameter(HttpServletRequest request) {
        HashMap<String, String> paramMap = new HashMap<>(10);
        paramMap.put(TERMINAL_INFO, Optional.ofNullable(request.getHeader(TERMINAL_INFO)).orElse(""));
        paramMap.put(COMPANY_ID, Optional.ofNullable(request.getHeader(COMPANY_ID)).orElse(""));
        paramMap.put(SYSTEM_ID, Optional.ofNullable(request.getHeader(SYSTEM_ID)).orElse(""));
        paramMap.put(EMPLOYEE_ID, Optional.ofNullable(request.getHeader(EMPLOYEE_ID)).orElse(""));
        paramMap.put(MODULE, Optional.ofNullable(request.getHeader(MODULE)).orElse(""));
        paramMap.put(PAGE, Optional.ofNullable(request.getHeader(PAGE)).orElse(""));
        paramMap.put(LOG_BASE_INFO_REDIS_KEY, Optional.ofNullable(request.getHeader(LOG_BASE_INFO_REDIS_KEY)).orElse(""));
        ThreadParameter.setLocalMap(paramMap);
    }
}
